// Copyright 2016 Tad Hardesty
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! High-level bindings for libopus.
//!
//! Only brief descriptions are included here. For detailed information, consult
//! the [libopus documentation](https://opus-codec.org/docs/opus_api-1.1.2/).
#![warn(missing_docs)]

mod opus_ffi;
use opus_ffi as ffi;


use std::ffi::CStr;
use std::marker::PhantomData;

use std::os::raw::c_int;

// ============================================================================
// Constants

// Generic CTLs
const OPUS_RESET_STATE: c_int = 4028; // void
const OPUS_GET_FINAL_RANGE: c_int = 4031; // out *u32
const OPUS_GET_BANDWIDTH: c_int = 4009; // out *i32
const OPUS_GET_SAMPLE_RATE: c_int = 4029; // out *i32
// Encoder CTLs
const OPUS_SET_BITRATE: c_int = 4002; // in i32
const OPUS_GET_BITRATE: c_int = 4003; // out *i32
const OPUS_SET_VBR: c_int = 4006; // in i32
const OPUS_GET_VBR: c_int = 4007; // out *i32
const OPUS_SET_VBR_CONSTRAINT: c_int = 4020; // in i32
const OPUS_GET_VBR_CONSTRAINT: c_int = 4021; // out *i32
const OPUS_SET_INBAND_FEC: c_int = 4012; // in i32
const OPUS_GET_INBAND_FEC: c_int = 4013; // out *i32
const OPUS_SET_PACKET_LOSS_PERC: c_int = 4014; // in i32
const OPUS_GET_PACKET_LOSS_PERC: c_int = 4015; // out *i32
const OPUS_SET_DTX_REQUEST: c_int = 4016;
const OPUS_GET_DTX_REQUEST: c_int = 4017;
const OPUS_GET_LOOKAHEAD: c_int = 4027; // out *i32
const OPUS_GET_IN_DTX_REQUEST: c_int = 4049;
// Decoder CTLs
const OPUS_SET_GAIN: c_int = 4034; // in i32
const OPUS_GET_GAIN: c_int = 4045; // out *i32
const OPUS_GET_LAST_PACKET_DURATION: c_int = 4039; // out *i32
const OPUS_GET_PITCH: c_int = 4033; // out *i32

// Bitrate
const OPUS_AUTO: c_int = -1000;
const OPUS_BITRATE_MAX: c_int = -1;

/// The possible applications for the codec.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum Application {
	/// Best for most VoIP/videoconference applications where listening quality
	/// and intelligibility matter most.
	Voip = 2048,
	/// Best for broadcast/high-fidelity application where the decoded audio
	/// should be as close as possible to the input.
	Audio = 2049,
	/// Only use when lowest-achievable latency is what matters most.
	LowDelay = 2051,
}

/// The available channel setings.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum Channels {
	/// One channel.
	Mono = 1,
	/// Two channels, left and right.
	Stereo = 2,
}

/// The available bandwidth level settings.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum Bandwidth {
	/// Auto/default setting.
	Auto = -1000,
	/// 4kHz bandpass.
	Narrowband = 1101,
	/// 6kHz bandpass.
	Mediumband = 1102,
	/// 8kHz bandpass.
	Wideband = 1103,
	/// 12kHz bandpass.
	Superwideband = 1104,
	/// 20kHz bandpass.
	Fullband = 1105,
}

impl Bandwidth {
	fn from_int(value: i32) -> Option<Bandwidth> {
		Some(match value {
			-1000 => Bandwidth::Auto,
			1101 => Bandwidth::Narrowband,
			1102 => Bandwidth::Mediumband,
			1103 => Bandwidth::Wideband,
			1104 => Bandwidth::Superwideband,
			1105 => Bandwidth::Fullband,
			_ => return None,
		})
	}

	fn decode(value: i32, what: &'static str) -> Result<Bandwidth> {
		match Bandwidth::from_int(value) {
			Some(bandwidth) => Ok(bandwidth),
			None => Err(Error::bad_arg(what)),
		}
	}
}

/// Possible error codes.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ErrorCode {
	/// One or more invalid/out of range arguments.
	BadArg = -1,
	/// Not enough bytes allocated in the buffer.
	BufferTooSmall = -2,
	/// An internal error was detected.
	InternalError = -3,
	/// The compressed data passed is corrupted.
	InvalidPacket = -4,
	/// Invalid/unsupported request number.
	Unimplemented = -5,
	/// An encoder or decoder structure is invalid or already freed.
	InvalidState = -6,
	/// Memory allocation has failed.
	AllocFail = -7,
	/// An unknown failure.
	Unknown = -8,
}

impl ErrorCode {
	fn from_int(value: c_int) -> ErrorCode {
		use ErrorCode::*;
		match value {
			ffi::OPUS_BAD_ARG => BadArg,
			ffi::OPUS_BUFFER_TOO_SMALL => BufferTooSmall,
			ffi::OPUS_INTERNAL_ERROR => InternalError,
			ffi::OPUS_INVALID_PACKET => InvalidPacket,
			ffi::OPUS_UNIMPLEMENTED => Unimplemented,
			ffi::OPUS_INVALID_STATE => InvalidState,
			ffi::OPUS_ALLOC_FAIL => AllocFail,
			_ => Unknown,
		}
	}

	/// Get a human-readable error string for this error code.
	pub fn description(self) -> &'static str {
		// should always be ASCII and non-null for any input
		unsafe { CStr::from_ptr(ffi::opus_strerror(self as c_int)) }.to_str().unwrap()
	}
}

/// Possible bitrates.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Bitrate {
	/// Explicit bitrate choice (in bits/second).
	Bits(i32),
	/// Maximum bitrate allowed (up to maximum number of bytes for the packet).
	Max,
	/// Default bitrate decided by the encoder (not recommended).
	Auto,
}

/// Get the libopus version string.
///
/// Applications may look for the substring "-fixed" in the version string to
/// determine whether they have a fixed-point or floating-point build at
/// runtime.
pub fn version() -> &'static str {
	// verison string should always be ASCII
	unsafe { CStr::from_ptr(ffi::opus_get_version_string()) }.to_str().unwrap()
}

macro_rules! ffi {
	($f:ident $(, $rest:expr)*) => {
		match unsafe { ffi::$f($($rest),*) } {
			code if code < 0 => return Err(Error::from_code(stringify!($f), code)),
			code => code,
		}
	}
}

macro_rules! ctl {
	($f:ident, $this:ident, $ctl:ident, $($rest:expr),*) => {
		match unsafe { ffi::$f($this.ptr, $ctl, $($rest),*) } {
			code if code < 0 => return Err(Error::from_code(
				concat!(stringify!($f), "(", stringify!($ctl), ")"),
				code,
			)),
			_ => (),
		}
	}
}

// ============================================================================
// Encoder

macro_rules! enc_ctl {
	($this:ident, $ctl:ident $(, $rest:expr)*) => {
		ctl!(opus_encoder_ctl, $this, $ctl, $($rest),*)
	}
}

/// An Opus encoder with associated state.
#[derive(Debug)]
pub struct Encoder {
	ptr: *mut ffi::OpusEncoder,
	channels: Channels,
}

impl Encoder {
	/// Create and initialize an encoder.
	pub fn new(sample_rate: u32, channels: Channels, mode: Application) -> Result<Encoder> {
		let mut error = 0;
		let ptr = unsafe { ffi::opus_encoder_create(
			sample_rate as i32,
			channels as c_int,
			mode as c_int,
			&mut error) };
		if error != ffi::OPUS_OK || ptr.is_null() {
			Err(Error::from_code("opus_encoder_create", error))
		} else {
			Ok(Encoder { ptr: ptr, channels: channels })
		}
	}

	/// Encode an Opus frame.
	pub fn encode(&mut self, input: &[i16], output: &mut [u8]) -> Result<usize> {
		let len = ffi!(opus_encode, self.ptr,
			input.as_ptr(), len(input) / self.channels as c_int,
			output.as_mut_ptr(), len(output));
		Ok(len as usize)
	}

	/// Encode an Opus frame from floating point input.
	pub fn encode_float(&mut self, input: &[f32], output: &mut [u8]) -> Result<usize> {
		let len = ffi!(opus_encode_float, self.ptr,
			input.as_ptr(), len(input) / self.channels as c_int,
			output.as_mut_ptr(), len(output));
		Ok(len as usize)
	}

	/// Encode an Opus frame to a new buffer.
	pub fn encode_vec(&mut self, input: &[i16], max_size: usize) -> Result<Vec<u8>> {
		let mut output: Vec<u8> = vec![0; max_size];
		let result = self.encode(input, output.as_mut_slice())?;
		output.truncate(result);
		Ok(output)
	}

	/// Encode an Opus frame from floating point input to a new buffer.
	pub fn encode_vec_float(&mut self, input: &[f32], max_size: usize) -> Result<Vec<u8>> {
		let mut output: Vec<u8> = vec![0; max_size];
		let result = self.encode_float(input, output.as_mut_slice())?;
		output.truncate(result);
		Ok(output)
	}

	// ------------
	// Generic CTLs

	/// Reset the codec state to be equivalent to a freshly initialized state.
	pub fn reset_state(&mut self) -> Result<()> {
		enc_ctl!(self, OPUS_RESET_STATE);
		Ok(())
	}

	/// Get the final range of the codec's entropy coder.
	pub fn get_final_range(&mut self) -> Result<u32> {
		let mut value: u32 = 0;
		enc_ctl!(self, OPUS_GET_FINAL_RANGE, &mut value);
		Ok(value)
	}

	/// Get the encoder's configured bandpass.
	pub fn get_bandwidth(&mut self) -> Result<Bandwidth> {
		let mut value: i32 = 0;
		enc_ctl!(self, OPUS_GET_BANDWIDTH, &mut value);
		Bandwidth::decode(value, "opus_encoder_ctl(OPUS_GET_BANDWIDTH)")
	}

	/// Get the samping rate the encoder was intialized with.
	pub fn get_sample_rate(&mut self) -> Result<u32> {
		let mut value: i32 = 0;
		enc_ctl!(self, OPUS_GET_SAMPLE_RATE, &mut value);
		Ok(value as u32)
	}

	// ------------
	// Encoder CTLs

	/// Set the encoder's bitrate.
	pub fn set_bitrate(&mut self, value: Bitrate) -> Result<()> {
		let value: i32 = match value {
			Bitrate::Auto => OPUS_AUTO,
			Bitrate::Max => OPUS_BITRATE_MAX,
			Bitrate::Bits(b) => b,
		};
		enc_ctl!(self, OPUS_SET_BITRATE, value);
		Ok(())
	}

	/// Get the encoder's bitrate.
	pub fn get_bitrate(&mut self) -> Result<Bitrate> {
		let mut value: i32 = 0;
		enc_ctl!(self, OPUS_GET_BITRATE, &mut value);
		Ok(match value {
			OPUS_AUTO => Bitrate::Auto,
			OPUS_BITRATE_MAX => Bitrate::Max,
			_ => Bitrate::Bits(value),
		})
	}

	/// Enable or disable variable bitrate.
	pub fn set_vbr(&mut self, vbr: bool) -> Result<()> {
		let value: i32 = if vbr { 1 } else { 0 };
		enc_ctl!(self, OPUS_SET_VBR, value);
		Ok(())
	}

	/// Determine if variable bitrate is enabled.
	pub fn get_vbr(&mut self) -> Result<bool> {
		let mut value: i32 = 0;
		enc_ctl!(self, OPUS_GET_VBR, &mut value);
		Ok(value != 0)
	}

	/// Enable or disable constrained VBR.
	pub fn set_vbr_constraint(&mut self, vbr: bool) -> Result<()> {
		let value: i32 = if vbr { 1 } else { 0 };
		enc_ctl!(self, OPUS_SET_VBR_CONSTRAINT, value);
		Ok(())
	}

	/// Determine if constrained VBR is enabled.
	pub fn get_vbr_constraint(&mut self) -> Result<bool> {
		let mut value: i32 = 0;
		enc_ctl!(self, OPUS_GET_VBR_CONSTRAINT, &mut value);
		Ok(value != 0)
	}

	/// Configures the encoder's use of inband forward error correction (FEC).
	pub fn set_inband_fec(&mut self, value: bool) -> Result<()> {
		let value: i32 = if value { 1 } else { 0 };
		enc_ctl!(self, OPUS_SET_INBAND_FEC, value);
		Ok(())
	}

	/// Gets encoder's configured use of inband forward error correction.
	pub fn get_inband_fec(&mut self) -> Result<bool> {
		let mut value: i32 = 0;
		enc_ctl!(self, OPUS_GET_INBAND_FEC, &mut value);
		Ok(value != 0)
	}

	/// Sets the encoder's expected packet loss percentage.
	pub fn set_packet_loss_perc(&mut self, value: i32) -> Result<()> {
		enc_ctl!(self, OPUS_SET_PACKET_LOSS_PERC, value);
		Ok(())
	}

	/// Gets the encoder's expected packet loss percentage.
	pub fn get_packet_loss_perc(&mut self) -> Result<i32> {
		let mut value: i32 = 0;
		enc_ctl!(self, OPUS_GET_PACKET_LOSS_PERC, &mut value);
		Ok(value)
	}

	/// Configures the encoder's use of discontinuous transmission (DTX).
	pub fn set_dtx(&mut self, value: bool) -> Result<()> {
		let value: i32 = if value { 1 } else { 0 };
		enc_ctl!(self, OPUS_SET_DTX_REQUEST, value);
		Ok(())
	}

	/// Gets encoder's configured use of discontinuous transmission. 
	pub fn get_dtx(&mut self) -> Result<bool> {
		let mut value: i32 = 0;
		enc_ctl!(self, OPUS_GET_DTX_REQUEST, &mut value);
		Ok(value != 0)
	}

	/// Gets the total samples of delay added by the entire codec.
	pub fn get_lookahead(&mut self) -> Result<i32> {
		let mut value: i32 = 0;
		enc_ctl!(self, OPUS_GET_LOOKAHEAD, &mut value);
		Ok(value)
	}

	/// Gets the DTX state of the encoder.
	/// Returns whether the last encoded frame was either a comfort noise update during DTX or not encoded because of DTX.
	pub fn get_in_dtx(&mut self) -> Result<bool> {
		let mut value: i32 = 0;
		enc_ctl!(self, OPUS_GET_IN_DTX_REQUEST, &mut value);
		Ok(value != 0)
	}


	// TODO: Encoder-specific CTLs
}

impl Drop for Encoder {
	fn drop(&mut self) {
		unsafe { ffi::opus_encoder_destroy(self.ptr) }
	}
}

// "A single codec state may only be accessed from a single thread at
// a time and any required locking must be performed by the caller. Separate
// streams must be decoded with separate decoder states and can be decoded
// in parallel unless the library was compiled with NONTHREADSAFE_PSEUDOSTACK
// defined."
//
// In other words, opus states may be moved between threads at will. A special
// compilation mode intended for embedded platforms forbids multithreaded use
// of the library as a whole rather than on a per-state basis, but the opus-sys
// crate does not use this mode.
unsafe impl Send for Encoder {}

// ============================================================================
// Decoder

macro_rules! dec_ctl {
	($this:ident, $ctl:ident $(, $rest:expr)*) => {
		ctl!(opus_decoder_ctl, $this, $ctl, $($rest),*)
	}
}

/// An Opus decoder with associated state.
#[derive(Debug)]
pub struct Decoder {
	ptr: *mut ffi::OpusDecoder,
	channels: Channels,
}

impl Decoder {
	/// Create and initialize a decoder.
	pub fn new(sample_rate: u32, channels: Channels) -> Result<Decoder> {
		let mut error = 0;
		let ptr = unsafe { ffi::opus_decoder_create(
			sample_rate as i32,
			channels as c_int,
			&mut error) };
		if error != ffi::OPUS_OK || ptr.is_null() {
			Err(Error::from_code("opus_decoder_create", error))
		} else {
			Ok(Decoder { ptr: ptr, channels: channels })
		}
	}

	/// Decode an Opus packet.
	pub fn decode(&mut self, input: &[u8], output: &mut [i16], fec: bool) -> Result<usize> {
		let ptr = match input.len() {
			0 => std::ptr::null(),
			_ => input.as_ptr(),
		};
		let len = ffi!(opus_decode, self.ptr,
			ptr, len(input),
			output.as_mut_ptr(), len(output) / self.channels as c_int,
			fec as c_int);
		Ok(len as usize)
	}

	/// Decode an Opus packet with floating point output.
	pub fn decode_float(&mut self, input: &[u8], output: &mut [f32], fec: bool) -> Result<usize> {
		let ptr = match input.len() {
			0 => std::ptr::null(),
			_ => input.as_ptr(),
		};
		let len = ffi!(opus_decode_float, self.ptr,
			ptr, len(input),
			output.as_mut_ptr(), len(output) / self.channels as c_int,
			fec as c_int);
		Ok(len as usize)
	}

	/// Get the number of samples of an Opus packet.
	pub fn get_nb_samples(&self, packet: &[u8]) -> Result<usize> {
		let len = ffi!(opus_decoder_get_nb_samples, self.ptr,
			packet.as_ptr(), packet.len() as i32);
		Ok(len as usize)
	}

	// ------------
	// Generic CTLs

	/// Reset the codec state to be equivalent to a freshly initialized state.
	pub fn reset_state(&mut self) -> Result<()> {
		dec_ctl!(self, OPUS_RESET_STATE);
		Ok(())
	}

	/// Get the final range of the codec's entropy coder.
	pub fn get_final_range(&mut self) -> Result<u32> {
		let mut value: u32 = 0;
		dec_ctl!(self, OPUS_GET_FINAL_RANGE, &mut value);
		Ok(value)
	}

	/// Get the decoder's last bandpass.
	pub fn get_bandwidth(&mut self) -> Result<Bandwidth> {
		let mut value: i32 = 0;
		dec_ctl!(self, OPUS_GET_BANDWIDTH, &mut value);
		Bandwidth::decode(value, "opus_decoder_ctl(OPUS_GET_BANDWIDTH)")
	}

	/// Get the samping rate the decoder was intialized with.
	pub fn get_sample_rate(&mut self) -> Result<u32> {
		let mut value: i32 = 0;
		dec_ctl!(self, OPUS_GET_SAMPLE_RATE, &mut value);
		Ok(value as u32)
	}

	// ------------
	// Decoder CTLs

	/// Configures decoder gain adjustment.
	///
	/// Scales the decoded output by a factor specified in Q8 dB units. This has
	/// a maximum range of -32768 to 32768 inclusive, and returns `BadArg`
	/// otherwise. The default is zero indicating no adjustment. This setting
	/// survives decoder reset.
	///
	/// `gain = pow(10, x / (20.0 * 256))`
	pub fn set_gain(&mut self, gain: i32) -> Result<()> {
		dec_ctl!(self, OPUS_SET_GAIN, gain);
		Ok(())
	}

	/// Gets the decoder's configured gain adjustment.
	pub fn get_gain(&mut self) -> Result<i32> {
		let mut value: i32 = 0;
		dec_ctl!(self, OPUS_GET_GAIN, &mut value);
		Ok(value)
	}

	/// Gets the duration (in samples) of the last packet successfully decoded
	/// or concealed.
	pub fn get_last_packet_duration(&mut self) -> Result<u32> {
		let mut value: i32 = 0;
		dec_ctl!(self, OPUS_GET_LAST_PACKET_DURATION, &mut value);
		Ok(value as u32)
	}

	/// Gets the pitch of the last decoded frame, if available.
	///
	/// This can be used for any post-processing algorithm requiring the use of
	/// pitch, e.g. time stretching/shortening. If the last frame was not
	/// voiced, or if the pitch was not coded in the frame, then zero is
	/// returned.
	pub fn get_pitch(&mut self) -> Result<i32> {
		let mut value: i32 = 0;
		dec_ctl!(self, OPUS_GET_PITCH, &mut value);
		Ok(value)
	}
}

impl Drop for Decoder {
	fn drop(&mut self) {
		unsafe { ffi::opus_decoder_destroy(self.ptr) }
	}
}

// See `unsafe impl Send for Encoder`.
unsafe impl Send for Decoder {}

// ============================================================================
// Packet Analysis

/// Analyze raw Opus packets.
pub mod packet {
	use super::*;
	use super::ffi;
	use std::{ptr, slice};

	/// Get the bandwidth of an Opus packet.
	pub fn get_bandwidth(packet: &[u8]) -> Result<Bandwidth> {
		if packet.len() < 1 {
			return Err(Error::bad_arg("opus_packet_get_bandwidth"));
		}
		let bandwidth = ffi!(opus_packet_get_bandwidth, packet.as_ptr());
		Bandwidth::decode(bandwidth, "opus_packet_get_bandwidth")
	}

	/// Get the number of channels from an Opus packet.
	pub fn get_nb_channels(packet: &[u8]) -> Result<Channels> {
		if packet.len() < 1 {
			return Err(Error::bad_arg("opus_packet_get_nb_channels"));
		}
		let channels = ffi!(opus_packet_get_nb_channels, packet.as_ptr());
		match channels {
			1 => Ok(Channels::Mono),
			2 => Ok(Channels::Stereo),
			_ => Err(Error::bad_arg("opus_packet_get_nb_channels")),
		}
	}

	/// Get the number of frames in an Opus packet.
	pub fn get_nb_frames(packet: &[u8]) -> Result<usize> {
		let frames = ffi!(opus_packet_get_nb_frames, packet.as_ptr(), len(packet));
		Ok(frames as usize)
	}

	/// Get the number of samples of an Opus packet.
	pub fn get_nb_samples(packet: &[u8], sample_rate: u32) -> Result<usize> {
		let frames = ffi!(opus_packet_get_nb_samples,
			packet.as_ptr(), len(packet),
			sample_rate as c_int);
		Ok(frames as usize)
	}

	/// Get the number of samples per frame from an Opus packet.
	pub fn get_samples_per_frame(packet: &[u8], sample_rate: u32) -> Result<usize> {
		if packet.len() < 1 {
			return Err(Error::bad_arg("opus_packet_get_samples_per_frame"))
		}
		let samples = ffi!(opus_packet_get_samples_per_frame,
			packet.as_ptr(), sample_rate as c_int);
		Ok(samples as usize)
	}

	/// Parse an Opus packet into one or more frames.
	pub fn parse(packet: &[u8]) -> Result<Packet> {
		let mut toc: u8 = 0;
		let mut frames = [ptr::null(); 48];
		let mut sizes = [0i16; 48];
		let mut payload_offset: i32 = 0;
		let num_frames = ffi!(opus_packet_parse,
			packet.as_ptr(), len(packet),
			&mut toc, frames.as_mut_ptr(),
			sizes.as_mut_ptr(), &mut payload_offset);

		let mut frames_vec = Vec::with_capacity(num_frames as usize);
		for i in 0..num_frames as usize {
			frames_vec.push(unsafe { slice::from_raw_parts(frames[i], sizes[i] as usize) });
		}

		Ok(Packet {
			toc: toc,
			frames: frames_vec,
			payload_offset: payload_offset as usize,
		})
	}

	/// A parsed Opus packet, retuned from `parse`.
	#[derive(Debug)]
	pub struct Packet<'a> {
		/// The TOC byte of the packet.
		pub toc: u8,
		/// The frames contained in the packet.
		pub frames: Vec<&'a [u8]>,
		/// The offset into the packet at which the payload is located.
		pub payload_offset: usize,
	}

	/// Pad a given Opus packet to a larger size.
	///
	/// The packet will be extended from the first `prev_len` bytes of the
	/// buffer into the rest of the available space.
	pub fn pad(packet: &mut [u8], prev_len: usize) -> Result<usize> {
		let result = ffi!(opus_packet_pad, packet.as_mut_ptr(),
			check_len(prev_len), len(packet));
		Ok(result as usize)
	}

	/// Remove all padding from a given Opus packet and rewrite the TOC sequence
	/// to minimize space usage.
	pub fn unpad(packet: &mut [u8]) -> Result<usize> {
		let result = ffi!(opus_packet_unpad, packet.as_mut_ptr(), len(packet));
		Ok(result as usize)
	}
}

// ============================================================================
// Float Soft Clipping

/// Soft-clipping to bring a float signal within the [-1,1] range.
#[derive(Debug)]
pub struct SoftClip {
	channels: Channels,
	memory: [f32; 2],
}

impl SoftClip {
	/// Initialize a new soft-clipping state.
	pub fn new(channels: Channels) -> SoftClip {
		SoftClip { channels: channels, memory: [0.0; 2] }
	}

	/// Apply soft-clipping to a float signal.
	pub fn apply(&mut self, signal: &mut [f32]) {
		unsafe { ffi::opus_pcm_soft_clip(
			signal.as_mut_ptr(),
			len(signal) / self.channels as c_int,
			self.channels as c_int,
			self.memory.as_mut_ptr()) };
	}
}

// ============================================================================
// Repacketizer

/// A repacketizer used to merge together or split apart multiple Opus packets.
#[derive(Debug)]
pub struct Repacketizer {
	ptr: *mut ffi::OpusRepacketizer,
}

impl Repacketizer {
	/// Create and initialize a repacketizer.
	pub fn new() -> Result<Repacketizer> {
		let ptr = unsafe { ffi::opus_repacketizer_create() };
		if ptr.is_null() {
			Err(Error::from_code("opus_repacketizer_create", ffi::OPUS_ALLOC_FAIL))
		} else {
			Ok(Repacketizer { ptr: ptr })
		}
	}

	/// Shortcut to combine several smaller packets into one larger one.
	pub fn combine(&mut self, input: &[&[u8]], output: &mut [u8]) -> Result<usize> {
		let mut state = self.begin();
		for &packet in input {
		state.cat(packet)?;
		}
		state.out(output)
	}

	/// Begin using the repacketizer.
	pub fn begin<'rp, 'buf>(&'rp mut self) -> RepacketizerState<'rp, 'buf> {
		unsafe { ffi::opus_repacketizer_init(self.ptr); }
		RepacketizerState { rp: self, phantom: PhantomData }
	}
}

impl Drop for Repacketizer {
	fn drop(&mut self) {
		unsafe { ffi::opus_repacketizer_destroy(self.ptr) }
	}
}

// See `unsafe impl Send for Encoder`.
unsafe impl Send for Repacketizer {}

// To understand why these lifetime bounds are needed, imagine that the
// repacketizer keeps an internal Vec<&'buf [u8]>, which is added to by cat()
// and accessed by get_nb_frames(), out(), and out_range(). To prove that these
// lifetime bounds are correct, a dummy implementation with the same signatures
// but a real Vec<&'buf [u8]> rather than unsafe blocks may be substituted.

/// An in-progress repacketization.
#[derive(Debug)]
pub struct RepacketizerState<'rp, 'buf> {
	rp: &'rp mut Repacketizer,
	phantom: PhantomData<&'buf [u8]>,
}

impl<'rp, 'buf> RepacketizerState<'rp, 'buf> {
	/// Add a packet to the current repacketizer state.
	pub fn cat(&mut self, packet: &'buf [u8]) -> Result<()> {
		ffi!(opus_repacketizer_cat, self.rp.ptr,
			packet.as_ptr(), len(packet));
		Ok(())
	}

	/// Add a packet to the current repacketizer state, moving it.
	#[inline]
	pub fn cat_move<'b2>(self, packet: &'b2 [u8]) -> Result<RepacketizerState<'rp, 'b2>> where 'buf: 'b2 {
		let mut shorter = self;
		shorter.cat(packet)?;
		Ok(shorter)
	}

	/// Get the total number of frames contained in packet data submitted so
	/// far via `cat`.
	pub fn get_nb_frames(&mut self) -> usize {
		unsafe { ffi::opus_repacketizer_get_nb_frames(self.rp.ptr) as usize }
	}

	/// Construct a new packet from data previously submitted via `cat`.
	///
	/// All previously submitted frames are used.
	pub fn out(&mut self, buffer: &mut [u8]) -> Result<usize> {
		let result = ffi!(opus_repacketizer_out, self.rp.ptr,
			buffer.as_mut_ptr(), len(buffer));
		Ok(result as usize)
	}

	/// Construct a new packet from data previously submitted via `cat`, with
	/// a manually specified subrange.
	///
	/// The `end` index should not exceed the value of `get_nb_frames()`.
	pub fn out_range(&mut self, begin: usize, end: usize, buffer: &mut [u8]) -> Result<usize> {
		let result = ffi!(opus_repacketizer_out_range, self.rp.ptr,
			check_len(begin), check_len(end),
			buffer.as_mut_ptr(), len(buffer));
		Ok(result as usize)
	}
}

// ============================================================================
// TODO: Multistream API

// ============================================================================
// Error Handling

/// Opus error Result alias.
pub type Result<T> = std::result::Result<T, Error>;

/// An error generated by the Opus library.
#[derive(Debug)]
pub struct Error {
	function: &'static str,
	code: ErrorCode,
}

impl Error {
	fn bad_arg(what: &'static str) -> Error {
		Error { function: what, code: ErrorCode::BadArg }
	}

	fn from_code(what: &'static str, code: c_int) -> Error {
		Error { function: what, code: ErrorCode::from_int(code) }
	}

	/// Get the name of the Opus function from which the error originated.
	#[inline]
	pub fn function(&self) -> &'static str { self.function }

	/// Get a textual description of the error provided by Opus.
	#[inline]
	pub fn description(&self) -> &'static str { self.code.description() }

	/// Get the Opus error code of the error.
	#[inline]
	pub fn code(&self) -> ErrorCode { self.code }
}

impl std::fmt::Display for Error {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		write!(f, "{}: {}", self.function, self.description())
	}
}

impl std::error::Error for Error {
	fn description(&self) -> &str {
		self.code.description()
	}
}

fn check_len(val: usize) -> c_int {
	let len = val as c_int;
	if len as usize != val {
		panic!("length out of range: {}", val);
	}
	len
}

#[inline]
fn len<T>(slice: &[T]) -> c_int {
	check_len(slice.len())
}
